import axios from 'axios';
import store from '@/store';
import loading from '@/utils/loading'
import {
    downloadFileByResponse,
    generateRandomString,
    resetLogin,
    showMessage,
    toFlattenDataSign
} from "@/utils";
import CryptoJS from 'crypto-js';
import rsaCrypto from "@/utils/rsa";

import {refreshAccessToken} from '@/utils/token';

// 根据当前环境 拿到地址 开发环境是dev 那么就在.env.dev中 找到对应的配置
const appVersion = process.env.VUE_APP_VERSION;
const apiUrl = process.env.VUE_APP_API_URL;
const apiSignEnabled = process.env.VUE_APP_API_SIGN_ENABLED;
const apiAccessKey = process.env.VUE_APP_API_ACCESS_KEY;
const apiSecretKey = process.env.VUE_APP_API_SECRET_KEY;
const apiEnabledRelativePath = process.env.VUE_APP_API_ENABLED_RELATIVE_PATH;

const responseEnabled = process.env.VUE_APP_RESPONSE_ENABLED;
const responsePrivateKey = process.env.VUE_APP_RESPONSE_PRIVATE_KEY;

const whiteList = [
    '/api/auth/oauth2/token',
    '/api/mgt/sms/sendCaptcha',
    '/api/mgt/wechat/getWechatMpQrCode',
    '/api/mgt/thirdBindGrant/editUser',
    '/api/mgt/user/saveBySms'
];

/**
 * axios配置
 */
const service = axios.create({
    baseURL: apiUrl,
    timeout: 30000,
    headers: {
        "Accept-Language": "en-US",
        'X-version': appVersion,
    },
});

/**
 * 全局请求拦截.
 */
service.interceptors.request.use(
    config => {
        if (config.isLoading) {
            // loading.open()
        }

        generateSign(config);

        if (!whiteList.includes(config.url)) {
            // 配置所有请求的token
            const {userToken} = store.state;
            const accessToken = userToken?.access_token;
            if (accessToken) {
                config.headers['Authorization'] = accessToken;
            }
        }

        return config;
    },
    error => {
        loading.close();
        return Promise.reject(error);
    }
);

/**
 * 全局响应拦截.
 */
service.interceptors.response.use(
    response => {
        loading.close();
        let data = response.data;
        if (data instanceof Blob) {
            if (data.type !== 'application/json') {
                downloadFileByResponse(response.headers['content-disposition'], data);
                return;
            }
            const reader = new FileReader();
            reader.onload = () => {
                const json = JSON.parse(reader.result);
                return showMessage(json.bizMessage);
            };
            reader.readAsText(data);
            return;
        }

        const bizData = data?.bizData;
        if (bizData) {
            data.bizData = decryptResponseData(bizData);
        }

        if (data?.bizCode === 'ok') {
            return data;
        }

        // 无效 token → 刷新后再重试
        if (data?.bizCode === 'invalid_access_token' && !response.config._retry) {
            return doRequest(response.config);
        }

        if (['ga_unbound', 'ga_captcha_not_empty', 'mail_captcha_not_empty', 'sms_captcha_not_empty'].includes(data?.bizCode)) {
            return showVerifyDialog(data.bizCode, response.config);
        }

        if (['invalid_refresh_token', 'user_has_switched_org'].includes(data?.bizCode)) {
            resetLogin();
            return;
        }

        const message = data?.bizMessage;
        if (message != null && message !== '') {
            return showMessage(message);
        }
        return Promise.reject(data);
    },
    error => {
        loading.close();
        const {response} = error;
        const status = response?.status;
        const message = response?.data?.bizMessage;

        if (status === 404) {
            return showMessage('请求地址不存在');
        }
        if ([502, 503].includes(status)) {
            return showMessage('服务不可用，请稍后再试');
        }
        if (message) {
            return showMessage(message);
        }
        return Promise.reject(response);
    }
);

/**
 * 生成签名，并设置到请求头
 * @param config
 */
const generateSign = function (config) {
    if (apiSignEnabled !== 'true') {
        return;
    }

    const method = config.method.toUpperCase();
    let path = config.url;

    // 是否启用相对路径
    if (apiEnabledRelativePath == 'true') {
        path = path.replace(/^\/[^/]+\/[^/]+/, '');
    }

    const accessKey = apiAccessKey;
    const timestamp = Date.now().toString();
    const nonce = generateRandomString(12);

    // 合并所有参数（GET/POST）
    const allParams = {
        'X-Access-Key': accessKey,
        'X-Timestamp': timestamp,
        'X-Nonce': nonce,
        'X-Method': method,
        'X-Path': path,
        ...(config.params || {}),
        ...(config.data || {})
    };

    // 过滤无效值
    Object.keys(allParams).forEach(k => {
        if (allParams[k] == null) delete allParams[k];
    });
    // 拼接为 key=value 形式
    const paramStr = toFlattenDataSign(allParams);
    // 打印调试信息
    // console.log('Sorted Param String:', paramStr);
    // 使用 HmacSHA256 加密
    const hmacSha256 = CryptoJS.HmacSHA256(paramStr, apiSecretKey).toString(CryptoJS.enc.Hex);
    // 再使用 MD5 对 HmacSHA256 结果进行哈希
    const sign = CryptoJS.MD5(hmacSha256);
    // 设置到 header
    config.headers["X-Access-Key"] = accessKey;
    config.headers["X-Timestamp"] = timestamp;
    config.headers["X-Nonce"] = nonce;
    config.headers["X-Sign"] = sign;
}

/**
 * 解密响应数据
 * @param encryptedData
 * @returns {*}
 */
const decryptResponseData = function (encryptedData) {
    // 1. 空值快速返回
    if (!encryptedData) {
        console.warn('decryptResponseData: encryptedData 为空，跳过解密');
        return null;
    }

    // 2. 未开启解密直接透传
    if (responseEnabled !== 'true') {
        return encryptedData;
    }

    // 解密数据
    const decryptedData = rsaCrypto.decrypt(responsePrivateKey, encryptedData);

    // console.log("解密后的数据:", decryptedData);
    return JSON.parse(decryptedData);
};

/**
 * 刷新token
 * @param config
 * @returns {Promise<unknown>}
 */
export const doRequest = async function (config) {
    config._retry = true;                 // 防止无限重试
    return new Promise((resolve, reject) => {
        // 1. 把“真正要重发的请求”封装成回调
        const retry = async () => {
            // ★★★ 每次重试都去拿最新 token
            const {accessToken} = await refreshAccessToken();
            config.headers = config.headers || {};
            config.headers.Authorization = accessToken;
            return service(config).then(resolve).catch(reject);
        };

        // 2. 塞进队列
        store.commit('addRetryRequest', retry);

        // 3. 触发刷新（第一个 401 抢到锁，其余仅排队）
        refreshAccessToken().catch(() => {
            /* 刷新失败已在内部退登 + 清空队列，这里要把原始请求 reject 掉 */
            reject();
        });
    });
};

/**
 * 统一验证弹窗（合并 showGaBindDialog / showGaCaptchaDialog / showMailCaptchaDialog / showSmsCaptchaDialog）
 * @param {string} bizCode  响应里带回的业务码
 * @param {AxiosRequestConfig} config  原请求配置
 * @returns {Promise<any>}
 */
const showVerifyDialog = (bizCode, config) => {
    // 1. 映射：bizCode -> 弹窗实例 & 对应 header
    const map = {
        // 绑定场景不需要验证码
        ga_unbound: {dialog: 'gaBindDialog', header: null},
        ga_captcha_not_empty: {dialog: 'gaCaptchaDialog', header: 'X-Ga-Captcha'},
        mail_captcha_not_empty: {dialog: 'mailCaptchaDialog', header: 'X-Mail-Captcha'},
        sms_captcha_not_empty: {dialog: 'smsCaptchaDialog', header: 'X-Sms-Captcha'}
    };

    const {dialog, header} = map[bizCode];
    if (!dialog) {
        return Promise.reject(new Error(`未处理的业务码: ${bizCode}`));
    }

    return new Promise((resolve, reject) => {
        // 2. 把 body 统一转成对象，防止后续重复判断
        if (typeof config.data === 'string') {
            try {
                config.data = JSON.parse(config.data);
            } catch {
            }
        }

        // 3. 弹窗
        window[dialog].show(async (captcha = '') => {
            // 需要验证码的场景才写入 header
            if (header) {
                config.headers[header] = captcha;
            }

            try {
                const resp = await service(config);
                if (resp?.bizCode === 'ok') {
                    resolve(resp);
                } else {
                    reject(resp);
                }
            } catch (err) {
                reject(err);
            } finally {
                window[dialog].closeDialog?.();
            }
        });
    });
};

export default service;
